D:\a\tools.proto\tools.proto\compiler\src\gen\base\structure.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::structure::{Field, FieldRaw, FieldType, FieldView, FixedField, FixedFieldType, Structure}; |
30 | | use crate::compiler::util::types::TypeMapper; |
31 | | use crate::gen::base::map::TypePathMapper; |
32 | | use crate::gen::template::hook::{Render, TemplateHooks}; |
33 | | use crate::gen::template::{Scope, Template}; |
34 | | use crate::model::protocol::{Description, Endianness}; |
35 | | use itertools::Itertools; |
36 | | use std::borrow::Cow; |
37 | | |
38 | | pub trait Utilities { |
39 | | fn get_field_type(field_type: FixedFieldType) -> &'static str; |
40 | | fn get_fragment_name(field: &Field) -> &'static str; |
41 | | fn get_fragment_name_mut(field: &Field) -> &'static str; |
42 | | fn get_bit_codec_inline(endianness: Endianness) -> &'static str; |
43 | | fn get_byte_codec_inline(endianness: Endianness) -> &'static str; |
44 | | fn get_byte_codec(endianness: Endianness) -> &'static str; |
45 | 0 | fn gen_description(desc: &Description) -> Cow<str> { |
46 | 0 | match desc { |
47 | 0 | Description::Single(v) => Cow::Borrowed(v), |
48 | 0 | Description::Multi(v) => Cow::Owned(v.join(" ")), |
49 | | } |
50 | 0 | } |
51 | | } |
52 | | |
53 | | enum Mode { |
54 | | Getter, |
55 | | Setter, |
56 | | } |
57 | | |
58 | | impl Mode { |
59 | 792 | pub fn get_path<'a>(&self, getter: &'a str, setter: &'a str) -> &'a str { |
60 | 792 | match self { |
61 | 396 | Mode::Getter => getter, |
62 | 396 | Mode::Setter => setter, |
63 | | } |
64 | 792 | } |
65 | | } |
66 | | |
67 | 702 | fn gen_structure_field_prologue<'a, 'fragment, 'variable: 'fragment, U: Utilities>( |
68 | 702 | template: &'variable Template<'fragment, 'variable>, |
69 | 702 | field: &'variable Field, |
70 | 702 | ) -> Scope<'a, 'fragment, 'variable> { |
71 | 702 | let mut scope = template.scope(); |
72 | 702 | scope |
73 | 702 | .var_d("start", field.loc.byte_offset) |
74 | 702 | .var_d("end", field.loc.byte_offset + field.loc.byte_size) |
75 | 702 | .var("name", &field.name) |
76 | 702 | .var( |
77 | 702 | "description", |
78 | 702 | field.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
79 | 702 | ) |
80 | 702 | .var_d("info", field); |
81 | 702 | scope |
82 | 702 | } |
83 | | |
84 | 468 | fn gen_structure_field<U: Utilities, G: FnMut(Mode, &Field, &FixedField, Scope) -> String>( |
85 | 468 | mode: Mode, |
86 | 468 | field: &Field, |
87 | 468 | template: &Template, |
88 | 468 | field_generator: &mut G, |
89 | 468 | ) -> Option<String> { |
90 | 468 | let mut scope = gen_structure_field_prologue::<U>(template, field); |
91 | 468 | match &field.ty { |
92 | 396 | FieldType::Fixed(v) => { |
93 | 396 | let bits_type = U::get_field_type(v.bits_type); |
94 | 396 | let raw_type = U::get_field_type(v.raw_type); |
95 | 396 | scope |
96 | 396 | .var("bits_type", bits_type) |
97 | 396 | .var("raw_type", raw_type) |
98 | 396 | .var_d("bit_offset", field.loc.bit_offset) |
99 | 396 | .var_d("bit_size", field.loc.bit_size); |
100 | 396 | Some(field_generator(mode, field, v, scope)) |
101 | | } |
102 | 72 | _ => None, |
103 | | } |
104 | 468 | } |
105 | | |
106 | 118 | fn gen_structure<'variable, U: Utilities, G: FnMut(Mode, &Field, &FixedField, Scope) -> String>( |
107 | 118 | s: &'variable Structure, |
108 | 118 | mut template: Template<'_, 'variable>, |
109 | 118 | mut field_generator: G, |
110 | 118 | ) -> String { |
111 | 118 | template.var("struct_name", &s.name).var( |
112 | 118 | "struct_description", |
113 | 118 | s.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
114 | 118 | ); |
115 | 118 | let getters = s |
116 | 118 | .fields |
117 | 118 | .iter() |
118 | 234 | .filter_map(|v| gen_structure_field::<U, G>(Mode::Getter, v, &template, &mut field_generator)) |
119 | 118 | .join(""); |
120 | 118 | let setters = s |
121 | 118 | .fields |
122 | 118 | .iter() |
123 | 234 | .filter_map(|v| gen_structure_field::<U, G>(Mode::Setter, v, &template, &mut field_generator)) |
124 | 118 | .join(""); |
125 | 118 | let mut code = template.render("", &["decl"]).unwrap(); |
126 | 118 | if !getters.is_empty() { Branch (126:8): [True: 53, False: 6]
Branch (126:8): [True: 53, False: 6]
Branch (126:8): [True: 0, False: 0]
Branch (126:8): [True: 0, False: 0]
Branch (126:8): [Folded - Ignored]
|
127 | 106 | code += &template.var("fields", getters).render("", &["getters"]).unwrap(); |
128 | 106 | }12 |
129 | 118 | if !setters.is_empty() { Branch (129:8): [True: 53, False: 6]
Branch (129:8): [True: 53, False: 6]
Branch (129:8): [True: 0, False: 0]
Branch (129:8): [True: 0, False: 0]
Branch (129:8): [Folded - Ignored]
|
130 | 106 | code += &template.var("fields", setters).render("", &["setters"]).unwrap(); |
131 | 106 | }12 |
132 | 118 | code |
133 | 118 | } |
134 | | |
135 | 198 | fn gen_field_raw<'variable>( |
136 | 198 | mode: Mode, |
137 | 198 | _: &'variable Field, |
138 | 198 | fixed: &'variable FixedField, |
139 | 198 | mut scope: Scope<'_, '_, 'variable>, |
140 | 198 | ) -> String { |
141 | 198 | let path = mode.get_path("getters.field", "setters.field"); |
142 | 198 | match &fixed.raw { |
143 | | FieldRaw::Transmute => { |
144 | 72 | if fixed.raw_type == FixedFieldType::Bool { Branch (144:16): [True: 24, False: 48]
Branch (144:16): [Folded - Ignored]
|
145 | 24 | scope.render_to_var(path, &["transmute_bool"], "fragment").unwrap() |
146 | | } else { |
147 | 48 | scope.render_to_var(path, &["transmute_other"], "fragment").unwrap() |
148 | | } |
149 | | } |
150 | 20 | FieldRaw::SignedCast(max_positive) => { |
151 | 20 | scope.var_d("max_positive", max_positive).render_to_var(path, &["signed"], "fragment").unwrap() |
152 | | } |
153 | 106 | FieldRaw::None => scope.render_to_var(path, &["none"], "fragment").unwrap(), |
154 | | }; |
155 | 198 | scope.render(mode.get_path("getters", "setters"), &["field"]).unwrap() |
156 | 198 | } |
157 | | |
158 | 198 | fn gen_field_bin<'variable, U: Utilities>( |
159 | 198 | mode: Mode, |
160 | 198 | field: &'variable Field, |
161 | 198 | fixed: &'variable FixedField, |
162 | 198 | mut scope: Scope<'_, '_, 'variable>, |
163 | 198 | ) -> String { |
164 | 198 | let fragment_name = U::get_fragment_name(field); |
165 | 198 | if field.loc.bit_size % 8 != 0 { Branch (165:8): [True: 64, False: 134]
Branch (165:8): [True: 0, False: 0]
Branch (165:8): [Folded - Ignored]
|
166 | 64 | let path = mode.get_path("getters.field.bit", "setters.field.bit"); |
167 | 64 | scope |
168 | 64 | .var("codec", U::get_bit_codec_inline(fixed.endianness)) |
169 | 64 | .render_to_var(path, &[fragment_name], "fragment") |
170 | 64 | .unwrap(); |
171 | 134 | } else { |
172 | 134 | let path = mode.get_path("getters.field.byte", "setters.field.byte"); |
173 | 134 | scope |
174 | 134 | .var("codec", U::get_byte_codec_inline(fixed.endianness)) |
175 | 134 | .render_to_var(path, &[fragment_name], "fragment") |
176 | 134 | .unwrap(); |
177 | 134 | } |
178 | 198 | scope.render(mode.get_path("getters", "setters"), &["field"]).unwrap() |
179 | 198 | } |
180 | | |
181 | 117 | fn gen_field_getter<U: Utilities, T: TypeMapper>( |
182 | 117 | field: &Field, |
183 | 117 | template: &Template, |
184 | 117 | type_path_map: &TypePathMapper<T>, |
185 | 117 | ) -> String { |
186 | 117 | let mut scope = gen_structure_field_prologue::<U>(template, field); |
187 | 117 | match &field.ty { |
188 | 99 | FieldType::Fixed(v) => gen_field_view_getter::<U, T>(v, &scope, type_path_map), |
189 | 6 | FieldType::Array(v) => scope |
190 | 6 | .var("raw_type", U::get_field_type(v.ty)) |
191 | 6 | .var("codec", U::get_byte_codec(v.endianness)) |
192 | 6 | .var_d("bit_size", v.item_bit_size) |
193 | 6 | .render("getters", &["array"]) |
194 | 6 | .unwrap(), |
195 | 12 | FieldType::Struct(v) => scope.var("type_name", type_path_map.get(v)).render("getters", &["struct"]).unwrap(), |
196 | | } |
197 | 117 | } |
198 | | |
199 | 117 | fn gen_field_setter<U: Utilities, T: TypeMapper>( |
200 | 117 | field: &Field, |
201 | 117 | template: &Template, |
202 | 117 | type_path_map: &TypePathMapper<T>, |
203 | 117 | ) -> String { |
204 | 117 | let mut scope = gen_structure_field_prologue::<U>(template, field); |
205 | 117 | match &field.ty { |
206 | 99 | FieldType::Fixed(v) => gen_field_view_setter::<U, T>(v, &scope, type_path_map), |
207 | 6 | FieldType::Array(v) => scope |
208 | 6 | .var("raw_type", U::get_field_type(v.ty)) |
209 | 6 | .var("codec", U::get_byte_codec(v.endianness)) |
210 | 6 | .var_d("bit_size", v.item_bit_size) |
211 | 6 | .render("setters", &["array"]) |
212 | 6 | .unwrap(), |
213 | 12 | FieldType::Struct(v) => scope.var("type_name", type_path_map.get(v)).render("setters", &["struct"]).unwrap(), |
214 | | } |
215 | 117 | } |
216 | | |
217 | 99 | fn gen_field_view_getter<U: Utilities, T: TypeMapper>( |
218 | 99 | field: &FixedField, |
219 | 99 | scope: &Scope, |
220 | 99 | type_path_map: &TypePathMapper<T>, |
221 | 99 | ) -> String { |
222 | 99 | let mut scope = scope.clone(); |
223 | 99 | scope.var("raw_type", U::get_field_type(field.raw_type)); |
224 | 99 | match &field.view { |
225 | 4 | FieldView::Float { a, b, .. } => scope |
226 | 4 | .var("view_type", U::get_field_type(field.view_type)) |
227 | 4 | .var("a", format!("{:?}", a)) |
228 | 4 | .var("b", format!("{:?}", b)) |
229 | 4 | .render("getters", &["view_float"]) |
230 | 4 | .unwrap(), |
231 | 6 | FieldView::Enum(r) => scope |
232 | 6 | .var("view_type", type_path_map.get(r)) |
233 | 6 | .var("repr_type", U::get_field_type(r.repr_type)) |
234 | 6 | .render("getters", &["view_enum"]) |
235 | 6 | .unwrap(), |
236 | 89 | FieldView::None => scope |
237 | 89 | .var("view_type", U::get_field_type(field.view_type)) |
238 | 89 | .render("getters", &["view_none"]) |
239 | 89 | .unwrap(), |
240 | | } |
241 | 99 | } |
242 | | |
243 | 99 | fn gen_field_view_setter<U: Utilities, T: TypeMapper>( |
244 | 99 | field: &FixedField, |
245 | 99 | scope: &Scope, |
246 | 99 | type_path_map: &TypePathMapper<T>, |
247 | 99 | ) -> String { |
248 | 99 | let mut scope = scope.clone(); |
249 | 99 | scope.var("raw_type", U::get_field_type(field.raw_type)); |
250 | 99 | match &field.view { |
251 | 4 | FieldView::Float { a_inv, b_inv, .. } => scope |
252 | 4 | .var("view_type", U::get_field_type(field.view_type)) |
253 | 4 | .var("a_inv", format!("{:?}", a_inv)) |
254 | 4 | .var("b_inv", format!("{:?}", b_inv)) |
255 | 4 | .render("setters", &["view_float"]) |
256 | 4 | .unwrap(), |
257 | 6 | FieldView::Enum(r) => scope |
258 | 6 | .var("view_type", type_path_map.get(r)) |
259 | 6 | .var("repr_type", U::get_field_type(r.repr_type)) |
260 | 6 | .render("setters", &["view_enum"]) |
261 | 6 | .unwrap(), |
262 | 89 | FieldView::None => scope |
263 | 89 | .var("view_type", U::get_field_type(field.view_type)) |
264 | 89 | .render("setters", &["view_none"]) |
265 | 89 | .unwrap(), |
266 | | } |
267 | 99 | } |
268 | | |
269 | 59 | fn gen_structure_getters<U: Utilities, T: TypeMapper>( |
270 | 59 | s: &Structure, |
271 | 59 | template: &Template, |
272 | 59 | type_path_map: &TypePathMapper<T>, |
273 | 59 | ) -> String { |
274 | 59 | let mut scope = template.scope(); |
275 | 117 | let fields = s.fields.iter().map(|v| gen_field_getter::<U, T>(v, template, type_path_map)).join(""); |
276 | 59 | scope.var("fields", fields).render("", &["getters"]).unwrap() |
277 | 59 | } |
278 | | |
279 | 59 | fn gen_structure_setters<U: Utilities, T: TypeMapper>( |
280 | 59 | s: &Structure, |
281 | 59 | template: &Template, |
282 | 59 | type_path_map: &TypePathMapper<T>, |
283 | 59 | ) -> String { |
284 | 59 | let mut scope = template.scope(); |
285 | 117 | let fields = s.fields.iter().map(|v| gen_field_setter::<U, T>(v, template, type_path_map)).join(""); |
286 | 59 | scope.var("fields", fields).render("", &["setters"]).unwrap() |
287 | 59 | } |
288 | | |
289 | | pub struct Templates<'fragment, 'variable> { |
290 | | pub field_template: Template<'fragment, 'variable>, |
291 | | pub template: Template<'fragment, 'variable>, |
292 | | pub bits_template: Template<'fragment, 'variable>, |
293 | | pub raw_template: Template<'fragment, 'variable>, |
294 | | } |
295 | | |
296 | 59 | pub fn generate<'variable, U: Utilities, T: TypeMapper>( |
297 | 59 | templates: Templates<'_, 'variable>, |
298 | 59 | s: &'variable Structure, |
299 | 59 | type_path_map: &TypePathMapper<T>, |
300 | 59 | hooks: &TemplateHooks, |
301 | 59 | ) -> String { |
302 | 59 | let mut template = templates.template; |
303 | 59 | let mut field_template = templates.field_template; |
304 | 59 | field_template.var("struct_name", &s.name).var( |
305 | 59 | "struct_description", |
306 | 59 | s.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
307 | 59 | ); |
308 | 59 | template.var("name", &s.name).var_d("byte_size", s.byte_size).var( |
309 | 59 | "struct_description", |
310 | 59 | s.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
311 | 59 | ); |
312 | 59 | let mut code = template.render("", &["decl", "new", "fixed_size", "write_to", "from_bytes"]).unwrap(); |
313 | 59 | for frag22 in hooks.get_fragments("ext") { |
314 | 22 | code += &template.render_frag(frag).unwrap(); |
315 | 22 | } |
316 | 59 | code += &gen_structure_getters::<U, T>(s, &field_template, type_path_map); |
317 | 59 | code += &gen_structure_setters::<U, T>(s, &field_template, type_path_map); |
318 | 198 | code += &gen_structure::<U, _>(s, templates.bits_template, |mode, field, fixed, scope| { |
319 | 198 | gen_field_bin::<U>(mode, field, fixed, scope) |
320 | 198 | }); |
321 | 198 | code += &gen_structure::<U, _>(s, templates.raw_template, |mode, field, fixed, scope| { |
322 | 198 | gen_field_raw(mode, field, fixed, scope) |
323 | 198 | }); |
324 | 59 | code |
325 | 59 | } |